/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2020 ARM Ltd.
 */
#ifndef ASM_PROCESSOR_H
#define ASM_PROCESSOR_H

#ifndef __ASSEMBLY__

#include <seminix/percpu.h>
#include <seminix/cache.h>
#include <asm/ptrace.h>
#include <asm/cpufeature.h>

static inline void cpu_relax(void)
{
    asm volatile("yield" ::: "memory");
}

/*
 * Prefetching support
 */
#define ARCH_HAS_PREFETCH
static inline void prefetch(const void *ptr)
{
    asm volatile("prfm pldl1keep, %a0\n" : : "p" (ptr));
}

#define ARCH_HAS_PREFETCHW
static inline void prefetchw(const void *ptr)
{
    asm volatile("prfm pstl1keep, %a0\n" : : "p" (ptr));
}

#define ARCH_HAS_SPINLOCK_PREFETCH
static inline void spin_lock_prefetch(const void *ptr)
{
    asm volatile("prfm pstl1strm, %a0" : : "p" (ptr));
}

struct cpu_context {
    unsigned long x19;
    unsigned long x20;
    unsigned long x21;
    unsigned long x22;
    unsigned long x23;
    unsigned long x24;
    unsigned long x25;
    unsigned long x26;
    unsigned long x27;
    unsigned long x28;
    unsigned long fp;
    unsigned long sp;
    unsigned long pc;
};

struct thread_struct {
    struct cpu_context	cpu_context;	/* cpu context */

    /*
     * Whitelisted fields for hardened usercopy:
     * Maintainers must ensure manually that this contains no
     * implicit padding.
     */
    struct {
        unsigned long	tp_value;	/* TLS register */
        unsigned long	tp2_value;
        struct user_fpsimd_state fpsimd_state;
    } uw;

    unsigned int		fpsimd_cpu;
    void			    *sve_state;	/* SVE registers, if any */
    unsigned int		sve_vl;		/* SVE vector length */
    unsigned int		sve_vl_onexec;	/* SVE vl after next exec */
    unsigned long		fault_address;	/* fault info */
    unsigned long		fault_code;	/* ESR_EL1 value */
};

/* Sync TPIDR_EL0 back to thread_struct for current */
extern void tls_preserve_current_state(void);

#define task_user_tls(t)	(&(t)->thread.uw.tp_value)

#define INIT_THREAD {				\
    .fpsimd_cpu = CONFIG_NR_CPUS,			\
}

static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
{
    memset(regs, 0, sizeof(*regs));
    forget_syscall(regs);
    regs->pc = pc;
}

static inline void start_thread(struct pt_regs *regs, unsigned long pc,
                unsigned long sp)
{
    start_thread_common(regs, pc);
    regs->pstate = PSR_MODE_EL0t;

    if (arm64_get_ssbd_state() != ARM64_SSBD_FORCE_ENABLE)
        regs->pstate |= PSR_SSBS_BIT;

    regs->sp = sp;
}

/* Forward declaration, a strange C thing */
struct task_struct;

/* Free all resources held by a thread. */
extern void release_thread(struct task_struct *);

unsigned long get_wchan(struct task_struct *p);

/* Thread switching */
extern struct task_struct *cpu_switch_to(struct task_struct *prev,
                     struct task_struct *next);

#define task_pt_regs(p) \
    ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1)

extern unsigned long __ro_after_init signal_minsigstksz; /* sigframe size */
extern void __init minsigstksz_setup(void);

/*
 * Not at the top of the file due to a direct #include cycle between
 * <asm/fpsimd.h> and <asm/processor.h>.  Deferring this #include
 * ensures that contents of processor.h are visible to fpsimd.h even if
 * processor.h is included first.
 *
 * These prctl helpers are the only things in this file that require
 * fpsimd.h.  The core code expects them to be in this header.
 */
#include <asm/fpsimd.h>

/* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */
#define SVE_SET_VL(arg)	sve_set_current_vl(arg)
#define SVE_GET_VL()	sve_get_current_vl()

#endif /* __ASSEMBLY__ */
#endif /* !ASM_PROCESSOR_H */
